#!/usr/bin/env python
# utils/build-script - The ultimate tool for building Swift -*- python -*-
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

from __future__ import print_function

import argparse
import multiprocessing
import os
import pipes
import platform
import sys
import time

import android.adb.commands

from swift_build_support.swift_build_support import (
    arguments,
    debug,
    diagnostics,
    host,
    migration,
    products,
    shell,
    tar,
    targets,
    workspace
)

from swift_build_support.swift_build_support.SwiftBuildSupport import (
    HOME,
    SWIFT_BUILD_ROOT,
    SWIFT_REPO_NAME,
    SWIFT_SOURCE_ROOT,
    get_all_preset_names,
    get_preset_options,  
)

from swift_build_support.swift_build_support.cmake import CMake
from swift_build_support.swift_build_support.targets import \
    StdlibDeploymentTarget
from swift_build_support.swift_build_support.toolchain import host_toolchain

build_script_impl = os.path.join(
    SWIFT_SOURCE_ROOT, SWIFT_REPO_NAME, "utils", "build-script-impl")


def exit_rejecting_arguments(message, parser=None):
    print(message, file=sys.stderr)
    if parser:
        parser.print_usage(sys.stderr)
    sys.exit(2)  # 2 is the same as `argparse` error exit code.


def call_without_sleeping(command, env=None, dry_run=False, echo=False):
    """
    Execute a command during which system sleep is disabled.

    By default, this ignores the state of the `shell.dry_run` flag.
    """

    # Disable system sleep, if possible.
    if platform.system() == 'Darwin':
        # Don't mutate the caller's copy of the arguments.
        command = ["caffeinate"] + list(command)

    shell.call(command, env=env, dry_run=dry_run, echo=echo)


class HostSpecificConfiguration(object):

    """Configuration information for an individual host."""

    def __init__(self, host_target, invocation):
        """Initialize for the given `host_target`."""

        # Compute the set of deployment targets to configure/build.
        args = invocation.args
        if host_target == args.host_target:
            # This host is the user's desired product, so honor the requested
            # set of targets to configure/build.
            stdlib_targets_to_configure = args.stdlib_deployment_targets
            if "all" in args.build_stdlib_deployment_targets:
                stdlib_targets_to_build = set(stdlib_targets_to_configure)
            else:
                stdlib_targets_to_build = set(
                    args.build_stdlib_deployment_targets).intersection(
                    set(args.stdlib_deployment_targets))
        else:
            # Otherwise, this is a host we are building as part of
            # cross-compiling, so we only need the target itself.
            stdlib_targets_to_configure = [host_target]
            stdlib_targets_to_build = set(stdlib_targets_to_configure)

        # Compute the lists of **CMake** targets for each use case (configure
        # vs. build vs. run) and the SDKs to configure with.
        self.sdks_to_configure = set()
        self.swift_stdlib_build_targets = []
        self.swift_test_run_targets = []
        self.swift_benchmark_build_targets = []
        self.swift_benchmark_run_targets = []
        for deployment_target_name in stdlib_targets_to_configure:
            # Get the target object.
            deployment_target = StdlibDeploymentTarget.get_target_for_name(
                deployment_target_name)
            if deployment_target is None:
                diagnostics.fatal("unknown target: %r" % (
                    deployment_target_name,))

            # Add the SDK to use.
            deployment_platform = deployment_target.platform
            self.sdks_to_configure.add(deployment_platform.sdk_name)

            # If we aren't actually building this target (only configuring
            # it), do nothing else.
            if deployment_target_name not in stdlib_targets_to_build:
                continue

            # Compute which actions are desired.
            build = (
                deployment_platform not in invocation.platforms_to_skip_build)
            test = (
                deployment_platform not in invocation.platforms_to_skip_test)
            test_host_only = None
            build_benchmark = build and deployment_target.supports_benchmark
            # FIXME: Note, `build-script-impl` computed a property here
            # w.r.t. testing, but it was actually unused.

            # For platforms which normally require a connected device to
            # test, the default behavior is to run tests that only require
            # the host (i.e., they do not attempt to execute).
            if deployment_platform.is_darwin and \
               deployment_platform.is_embedded and \
               not deployment_platform.is_simulator:
                if deployment_platform not in \
                        invocation.platforms_to_skip_test_host:
                    test_host_only = True
                    test = True
                else:
                    test = False

            name = deployment_target.name
            if build:
                # Validation and long tests require building the full standard
                # library, whereas the other targets can build a slightly
                # smaller subset which is faster to build.
                if args.build_swift_stdlib_unittest_extra or \
                        args.validation_test or args.long_test:
                    self.swift_stdlib_build_targets.append(
                        "swift-stdlib-" + name)
                else:
                    self.swift_stdlib_build_targets.append(
                        "swift-test-stdlib-" + name)
            if build_benchmark:
                self.swift_benchmark_build_targets.append(
                    "swift-benchmark-" + name)
                # FIXME: This probably should respect `args.benchmark`, but
                # a typo in build-script-impl meant we always would do this.
                self.swift_benchmark_run_targets.append(
                    "check-swift-benchmark-" + name)
            if test:
                if test_host_only:
                    suffix = "-non-executable"
                else:
                    suffix = ""
                subset_suffix = ""
                if args.validation_test and args.long_test:
                    subset_suffix = "-all"
                elif args.validation_test:
                    subset_suffix = "-validation"
                elif args.long_test:
                    subset_suffix = "-only_long"
                else:
                    subset_suffix = ""
                self.swift_test_run_targets.append("check-swift{}{}-{}".format(
                    subset_suffix, suffix, name))
                if args.test_optimized and not test_host_only:
                    self.swift_test_run_targets.append(
                        "check-swift{}-optimize-{}".format(
                            subset_suffix, name))


class BuildScriptInvocation(object):

    """Represent a single build script invocation."""

    @staticmethod
    def validate_arguments(toolchain, args):
        if toolchain.cc is None or toolchain.cxx is None:
            diagnostics.fatal(
                "can't find clang (please install clang-3.5 or a "
                "later version)")

        if toolchain.cmake is None:
            diagnostics.fatal("can't find CMake (please install CMake)")

        if args.distcc:
            if toolchain.distcc is None:
                diagnostics.fatal(
                    "can't find distcc (please install distcc)")
            if toolchain.distcc_pump is None:
                diagnostics.fatal(
                    "can't find distcc-pump (please install distcc-pump)")

        if args.host_target is None or args.stdlib_deployment_targets is None:
            diagnostics.fatal("unknown operating system")

        if args.symbols_package:
            if not os.path.isabs(args.symbols_package):
                print(
                    '--symbols-package must be an absolute path '
                    '(was \'{}\')'.format(args.symbols_package))
                return 1
            if not args.install_symroot:
                diagnostics.fatal(
                    "--install-symroot is required when specifying "
                    "--symbols-package.")

        if args.android:
            if args.android_ndk is None or \
                    args.android_api_level is None or \
                    args.android_icu_uc is None or \
                    args.android_icu_uc_include is None or \
                    args.android_icu_i18n is None or \
                    args.android_icu_i18n_include is None:
                diagnostics.fatal(
                    "when building for Android, --android-ndk, "
                    "--android-ndk-version, --android-icu-uc, "
                    "--android-icu-uc-include, --android-icu-i18n, "
                    "and --android-icu-i18n-include must be specified")

    @staticmethod
    def apply_default_arguments(toolchain, args):
        """Preprocess an argument set to apply default behaviors."""

        # Build cmark if any cmark-related options were specified.
        if (args.cmark_build_variant is not None):
            args.build_cmark = True

        # Build LLDB if any LLDB-related options were specified.
        if args.lldb_build_variant is not None or \
           args.lldb_assertions is not None:
            args.build_lldb = True

        # Set the default build variant.
        if args.build_variant is None:
            args.build_variant = "Debug"

        # Set the default stdlib-deployment-targets, if none were provided.
        if args.stdlib_deployment_targets is None:
            stdlib_targets = \
                StdlibDeploymentTarget.default_stdlib_deployment_targets()
            args.stdlib_deployment_targets = [
                target.name for target in stdlib_targets]

        if args.llvm_build_variant is None:
            args.llvm_build_variant = args.build_variant

        if args.swift_build_variant is None:
            args.swift_build_variant = args.build_variant

        if args.swift_stdlib_build_variant is None:
            args.swift_stdlib_build_variant = args.build_variant

        if args.cmark_build_variant is None:
            args.cmark_build_variant = args.swift_build_variant

        if args.lldb_build_variant is None:
            args.lldb_build_variant = args.build_variant

        if args.foundation_build_variant is None:
            args.foundation_build_variant = args.build_variant

        if args.libdispatch_build_variant is None:
            args.libdispatch_build_variant = args.build_variant

        if args.libicu_build_variant is None:
            args.libicu_build_variant = args.build_variant

        # Assertions are enabled by default.
        if args.assertions is None:
            args.assertions = True

        # Propagate the default assertions setting.
        if args.cmark_assertions is None:
            args.cmark_assertions = args.assertions

        if args.llvm_assertions is None:
            args.llvm_assertions = args.assertions

        if args.swift_assertions is None:
            args.swift_assertions = args.assertions

        if args.swift_stdlib_assertions is None:
            args.swift_stdlib_assertions = args.assertions

        # Set the default CMake generator.
        if args.cmake_generator is None:
            args.cmake_generator = "Ninja"

        # --ios-all etc are not supported by open-source Swift.
        if args.ios_all:
            exit_rejecting_arguments("error: --ios-all is unavailable in " +
                                     "open-source Swift.\nUse --ios to " +
                                     "skip iOS device tests.")

        if args.tvos_all:
            exit_rejecting_arguments("error: --tvos-all is unavailable in " +
                                     "open-source Swift.\nUse --tvos to " +
                                     "skip tvOS device tests.")

        if args.watchos_all:
            exit_rejecting_arguments("error: --watchos-all is unavailable " +
                                     "in open-source Swift.\nUse --watchos " +
                                     "to skip watchOS device tests.")

        ninja_required = (
            args.cmake_generator == 'Ninja' or args.build_foundation)
        if ninja_required and toolchain.ninja is None:
            args.build_ninja = True

        # SwiftPM and XCTest have a dependency on Foundation.
        # On OS X, Foundation is built automatically using xcodebuild.
        # On Linux, we must ensure that it is built manually.
        if ((args.build_swiftpm or args.build_xctest) and
                platform.system() != "Darwin"):
            args.build_foundation = True

        # Foundation has a dependency on libdispatch.
        # On OS X, libdispatch is provided by the OS.
        # On Linux, we must ensure that it is built manually.
        if (args.build_foundation and
                platform.system() != "Darwin"):
            args.build_libdispatch = True

        # Propagate global --skip-build
        if args.skip_build:
            args.skip_build_linux = True
            args.skip_build_freebsd = True
            args.skip_build_cygwin = True
            args.skip_build_osx = True
            args.skip_build_ios = True
            args.skip_build_tvos = True
            args.skip_build_watchos = True
            args.skip_build_android = True
            args.skip_build_benchmarks = True
            args.build_lldb = False
            args.build_llbuild = False
            args.build_swiftpm = False
            args.build_xctest = False
            args.build_foundation = False
            args.build_libdispatch = False
            args.build_libicu = False
            args.build_playgroundlogger = False
            args.build_playgroundsupport = False

        # --skip-{ios,tvos,watchos} or --skip-build-{ios,tvos,watchos} are
        # merely shorthands for --skip-build-{**os}-{device,simulator}
        if not args.ios or args.skip_build_ios:
            args.skip_build_ios_device = True
            args.skip_build_ios_simulator = True

        if not args.tvos or args.skip_build_tvos:
            args.skip_build_tvos_device = True
            args.skip_build_tvos_simulator = True

        if not args.watchos or args.skip_build_watchos:
            args.skip_build_watchos_device = True
            args.skip_build_watchos_simulator = True

        if not args.android or args.skip_build_android:
            args.skip_build_android = True

        # --validation-test implies --test.
        if args.validation_test:
            args.test = True

        # --test-optimized implies --test.
        if args.test_optimized:
            args.test = True

        # If none of tests specified skip swift stdlib test on all platforms
        if not args.test and not args.validation_test and not args.long_test:
            args.skip_test_linux = True
            args.skip_test_freebsd = True
            args.skip_test_cygwin = True
            args.skip_test_osx = True
            args.skip_test_ios = True
            args.skip_test_tvos = True
            args.skip_test_watchos = True

        # --skip-test-ios is merely a shorthand for host and simulator tests.
        if args.skip_test_ios:
            args.skip_test_ios_host = True
            args.skip_test_ios_simulator = True
        # --skip-test-tvos is merely a shorthand for host and simulator tests.
        if args.skip_test_tvos:
            args.skip_test_tvos_host = True
            args.skip_test_tvos_simulator = True
        # --skip-test-watchos is merely a shorthand for host and simulator
        # --tests.
        if args.skip_test_watchos:
            args.skip_test_watchos_host = True
            args.skip_test_watchos_simulator = True

        # --skip-build-{ios,tvos,watchos}-{device,simulator} implies
        # --skip-test-{ios,tvos,watchos}-{host,simulator}
        if args.skip_build_ios_device:
            args.skip_test_ios_host = True
        if args.skip_build_ios_simulator:
            args.skip_test_ios_simulator = True

        if args.skip_build_tvos_device:
            args.skip_test_tvos_host = True
        if args.skip_build_tvos_simulator:
            args.skip_test_tvos_simulator = True

        if args.skip_build_watchos_device:
            args.skip_test_watchos_host = True
        if args.skip_build_watchos_simulator:
            args.skip_test_watchos_simulator = True

        if args.skip_build_android:
            args.skip_test_android_host = True

        if not args.host_test:
            args.skip_test_ios_host = True
            args.skip_test_tvos_host = True
            args.skip_test_watchos_host = True
            args.skip_test_android_host = True

        if args.build_subdir is None:
            args.build_subdir = \
                workspace.compute_build_subdir(args)

        # Add optional stdlib-deployment-targets
        if args.android:
            args.stdlib_deployment_targets.append(
                StdlibDeploymentTarget.Android.armv7.name)

        # Infer platform flags from manually-specified configure targets.
        # This doesn't apply to Darwin platforms, as they are
        # already configured. No building without the platform flag, though.

        android_tgts = [tgt for tgt in args.stdlib_deployment_targets
                        if StdlibDeploymentTarget.Android.contains(tgt)]
        if not args.android and len(android_tgts) > 0:
            args.android = True
            args.skip_build_android = True

# ---

    def __init__(self, toolchain, args):
        self.toolchain = toolchain
        self.args = args

        self.workspace = workspace.Workspace(
            source_root=SWIFT_SOURCE_ROOT,
            build_root=os.path.join(SWIFT_BUILD_ROOT, args.build_subdir))

        # Compute derived information from the arguments.
        #
        # FIXME: We should move the platform-derived arguments to be entirely
        # data driven, so that we can eliminate this code duplication and just
        # iterate over all supported platforms.

        self.platforms_to_skip_build = set()
        if args.skip_build_linux:
            self.platforms_to_skip_build.add(StdlibDeploymentTarget.Linux)
        if args.skip_build_freebsd:
            self.platforms_to_skip_build.add(StdlibDeploymentTarget.FreeBSD)
        if args.skip_build_cygwin:
            self.platforms_to_skip_build.add(StdlibDeploymentTarget.Cygwin)
        if args.skip_build_osx:
            self.platforms_to_skip_build.add(StdlibDeploymentTarget.OSX)
        if args.skip_build_ios_device:
            self.platforms_to_skip_build.add(StdlibDeploymentTarget.iOS)
        if args.skip_build_ios_simulator:
            self.platforms_to_skip_build.add(
                StdlibDeploymentTarget.iOSSimulator)
        if args.skip_build_tvos_device:
            self.platforms_to_skip_build.add(StdlibDeploymentTarget.AppleTV)
        if args.skip_build_tvos_simulator:
            self.platforms_to_skip_build.add(
                StdlibDeploymentTarget.AppleTVSimulator)
        if args.skip_build_watchos_device:
            self.platforms_to_skip_build.add(StdlibDeploymentTarget.AppleWatch)
        if args.skip_build_watchos_simulator:
            self.platforms_to_skip_build.add(
                StdlibDeploymentTarget.AppleWatchSimulator)
        if args.skip_build_android:
            self.platforms_to_skip_build.add(StdlibDeploymentTarget.Android)

        self.platforms_to_skip_test = set()
        if args.skip_test_linux:
            self.platforms_to_skip_test.add(StdlibDeploymentTarget.Linux)
        if args.skip_test_freebsd:
            self.platforms_to_skip_test.add(StdlibDeploymentTarget.FreeBSD)
        if args.skip_test_cygwin:
            self.platforms_to_skip_test.add(StdlibDeploymentTarget.Cygwin)
        if args.skip_test_osx:
            self.platforms_to_skip_test.add(StdlibDeploymentTarget.OSX)
        if args.skip_test_ios_host:
            self.platforms_to_skip_test.add(StdlibDeploymentTarget.iOS)
        else:
            exit_rejecting_arguments("error: iOS device tests are not " +
                                     "supported in open-source Swift.")
        if args.skip_test_ios_simulator:
            self.platforms_to_skip_test.add(
                StdlibDeploymentTarget.iOSSimulator)
        if args.skip_test_tvos_host:
            self.platforms_to_skip_test.add(StdlibDeploymentTarget.AppleTV)
        else:
            exit_rejecting_arguments("error: tvOS device tests are not " +
                                     "supported in open-source Swift.")
        if args.skip_test_tvos_simulator:
            self.platforms_to_skip_test.add(
                StdlibDeploymentTarget.AppleTVSimulator)
        if args.skip_test_watchos_host:
            self.platforms_to_skip_test.add(StdlibDeploymentTarget.AppleWatch)
        else:
            exit_rejecting_arguments("error: watchOS device tests are not " +
                                     "supported in open-source Swift.")
        if args.skip_test_watchos_simulator:
            self.platforms_to_skip_test.add(
                StdlibDeploymentTarget.AppleWatchSimulator)

        if args.skip_test_android_host:
            self.platforms_to_skip_test.add(StdlibDeploymentTarget.Android)

        self.platforms_to_skip_test_host = set()
        if args.skip_test_ios_host:
            self.platforms_to_skip_test_host.add(StdlibDeploymentTarget.iOS)
        if args.skip_test_tvos_host:
            self.platforms_to_skip_test_host.add(
                StdlibDeploymentTarget.AppleTV)
        if args.skip_test_watchos_host:
            self.platforms_to_skip_test_host.add(
                StdlibDeploymentTarget.AppleWatch)

    def initialize_runtime_environment(self):
        """Change the program environment for building."""

        # Set an appropriate default umask.
        os.umask(0o022)

        # Unset environment variables that might affect how tools behave.
        for v in [
                'MAKEFLAGS',
                'SDKROOT',
                'MACOSX_DEPLOYMENT_TARGET',
                'IPHONEOS_DEPLOYMENT_TARGET',
                'TVOS_DEPLOYMENT_TARGET',
                'WATCHOS_DEPLOYMENT_TARGET']:
            os.environ.pop(v, None)

    def build_ninja(self):
        if not os.path.exists(self.workspace.source_dir("ninja")):
            diagnostics.fatal(
                "can't find source directory for ninja "
                "(tried %s)" % (self.workspace.source_dir("ninja")))

        ninja_build = products.Ninja(
            args=self.args,
            toolchain=self.toolchain,
            source_dir=self.workspace.source_dir("ninja"),
            build_dir=self.workspace.build_dir("build", "ninja"))
        ninja_build.do_build()
        self.toolchain.ninja = ninja_build.ninja_bin_path

    def convert_to_impl_arguments(self):
        """convert_to_impl_arguments() -> (env, args)

        Convert the invocation to an environment and list of arguments suitable
        for invoking `build-script-impl`.
        """

        # Create local shadows, for convenience.
        args = self.args
        toolchain = self.toolchain

        cmake = CMake(args=args,
                      toolchain=self.toolchain)

        impl_args = [
            "--workspace", self.workspace.source_root,
            "--build-dir", self.workspace.build_root,
            "--install-prefix", args.install_prefix,
            "--host-target", args.host_target,
            "--stdlib-deployment-targets",
            " ".join(args.stdlib_deployment_targets),
            "--host-cc", toolchain.cc,
            "--host-cxx", toolchain.cxx,
            "--darwin-xcrun-toolchain", args.darwin_xcrun_toolchain,
            "--darwin-deployment-version-osx=%s" % (
                args.darwin_deployment_version_osx),
            "--darwin-deployment-version-ios=%s" % (
                args.darwin_deployment_version_ios),
            "--darwin-deployment-version-tvos=%s" % (
                args.darwin_deployment_version_tvos),
            "--darwin-deployment-version-watchos=%s" % (
                args.darwin_deployment_version_watchos),
            "--cmake", toolchain.cmake,
            "--cmark-build-type", args.cmark_build_variant,
            "--llvm-build-type", args.llvm_build_variant,
            "--swift-build-type", args.swift_build_variant,
            "--swift-stdlib-build-type", args.swift_stdlib_build_variant,
            "--lldb-build-type", args.lldb_build_variant,
            "--foundation-build-type", args.foundation_build_variant,
            "--libdispatch-build-type", args.libdispatch_build_variant,
            "--libicu-build-type", args.libicu_build_variant,
            "--xctest-build-type", args.build_variant,
            "--swiftpm-build-type", args.build_variant,
            "--swift-enable-assertions", str(args.swift_assertions).lower(),
            "--swift-stdlib-enable-assertions", str(
                args.swift_stdlib_assertions).lower(),
            "--swift-analyze-code-coverage", str(
                args.swift_analyze_code_coverage).lower(),
            "--cmake-generator", args.cmake_generator,
            "--build-jobs", str(args.build_jobs),
            "--common-cmake-options=%s" % ' '.join(
                pipes.quote(opt) for opt in cmake.common_options()),
            "--build-args=%s" % ' '.join(
                pipes.quote(arg) for arg in cmake.build_args()),
        ]

        # Compute any product specific cmake arguments.
        for product_class in self.compute_product_classes():
            product_name = product_class.product_name()
            product_source_name = product_class.product_source_name()
            source_dir = self.workspace.source_dir(product_source_name)

            if not os.path.exists(source_dir):
                diagnostics.fatal(
                    "can't find source directory for %s "
                    "(tried %s)" % (product_name, source_dir))

            product = product_class(
                args=args,
                toolchain=self.toolchain,
                source_dir=source_dir,
                # FIXME: This is incorrect since it always assumes the host
                # target I think?
                build_dir=self.workspace.build_dir(
                    args.host_target, product_name))
            cmake_opts = product.cmake_options

            # FIXME: We should be using pipes.quote here but we run into issues
            # with build-script-impl/cmake not being happy with all of the
            # extra "'" in the strings. To fix this easily, we really need to
            # just invoke cmake from build-script directly rather than futzing
            # with build-script-impl. This makes even more sense since there
            # really isn't a security issue here.
            impl_args += [
                "--%s-cmake-options=%s" % (product_name, ' '.join(cmake_opts))
            ]

        if args.build_stdlib_deployment_targets:
            impl_args += [
                "--build-stdlib-deployment-targets", " ".join(
                    args.build_stdlib_deployment_targets)]
        if args.cross_compile_hosts:
            impl_args += [
                "--cross-compile-hosts", " ".join(args.cross_compile_hosts)]

        if toolchain.ninja:
            impl_args += ["--ninja-bin=%s" % toolchain.ninja]
        if args.distcc:
            impl_args += [
                "--distcc",
                "--distcc-pump=%s" % toolchain.distcc_pump
            ]

        # *NOTE* We use normal cmake to pass through tsan/ubsan options. We do
        # NOT pass them to build-script-impl.
        if args.enable_asan:
            impl_args += ["--enable-asan"]
        if args.verbose_build:
            impl_args += ["--verbose-build"]
        if args.install_symroot:
            impl_args += [
                "--install-symroot", os.path.abspath(args.install_symroot)
            ]

        if args.skip_build:
            impl_args += ["--skip-build-cmark",
                          "--skip-build-llvm",
                          "--skip-build-swift"]
        if args.skip_build_benchmarks:
            impl_args += ["--skip-build-benchmarks"]
        if not args.build_foundation:
            impl_args += ["--skip-build-foundation"]
        if not args.build_xctest:
            impl_args += ["--skip-build-xctest"]
        if not args.build_lldb:
            impl_args += ["--skip-build-lldb"]
        if not args.build_llbuild:
            impl_args += ["--skip-build-llbuild"]
        if not args.build_libdispatch:
            impl_args += ["--skip-build-libdispatch"]
        if not args.build_libicu:
            impl_args += ["--skip-build-libicu"]
        if not args.build_swiftpm:
            impl_args += ["--skip-build-swiftpm"]
        if not args.build_playgroundlogger:
            impl_args += ["--skip-build-playgroundlogger"]
        if not args.build_playgroundsupport:
            impl_args += ["--skip-build-playgroundsupport"]
        if args.build_swift_dynamic_stdlib:
            impl_args += ["--build-swift-dynamic-stdlib"]
        if args.build_swift_static_stdlib:
            impl_args += ["--build-swift-static-stdlib"]
        if args.build_swift_stdlib_unittest_extra:
            impl_args += ["--build-swift-stdlib-unittest-extra"]
        if args.build_swift_dynamic_sdk_overlay:
            impl_args += ["--build-swift-dynamic-sdk-overlay"]
        if args.build_swift_static_sdk_overlay:
            impl_args += ["--build-swift-static-sdk-overlay"]

        if args.skip_build_linux:
            impl_args += ["--skip-build-linux"]
        if args.skip_build_freebsd:
            impl_args += ["--skip-build-freebsd"]
        if args.skip_build_cygwin:
            impl_args += ["--skip-build-cygwin"]
        if args.skip_build_osx:
            impl_args += ["--skip-build-osx"]
        if args.skip_build_ios_device:
            impl_args += ["--skip-build-ios-device"]
        if args.skip_build_ios_simulator:
            impl_args += ["--skip-build-ios-simulator"]
        if args.skip_build_tvos_device:
            impl_args += ["--skip-build-tvos-device"]
        if args.skip_build_tvos_simulator:
            impl_args += ["--skip-build-tvos-simulator"]
        if args.skip_build_watchos_device:
            impl_args += ["--skip-build-watchos-device"]
        if args.skip_build_watchos_simulator:
            impl_args += ["--skip-build-watchos-simulator"]
        if args.skip_build_android:
            impl_args += ["--skip-build-android"]

        if not args.test and not args.long_test:
            impl_args += ["--skip-test-swift"]
        if not args.test:
            impl_args += ["--skip-test-cmark",
                          "--skip-test-lldb",
                          "--skip-test-llbuild",
                          "--skip-test-swiftpm",
                          "--skip-test-xctest",
                          "--skip-test-foundation",
                          "--skip-test-libdispatch",
                          "--skip-test-libicu",
                          "--skip-test-playgroundlogger",
                          "--skip-test-playgroundsupport"]
        if args.skip_test_linux:
            impl_args += ["--skip-test-linux"]
        if args.skip_test_freebsd:
            impl_args += ["--skip-test-freebsd"]
        if args.skip_test_cygwin:
            impl_args += ["--skip-test-cygwin"]
        if args.skip_test_osx:
            impl_args += ["--skip-test-osx"]
        if args.skip_test_ios_host:
            impl_args += ["--skip-test-ios-host"]
        if args.skip_test_ios_simulator:
            impl_args += ["--skip-test-ios-simulator"]
        if args.skip_test_tvos_host:
            impl_args += ["--skip-test-tvos-host"]
        if args.skip_test_tvos_simulator:
            impl_args += ["--skip-test-tvos-simulator"]
        if args.skip_test_watchos_host:
            impl_args += ["--skip-test-watchos-host"]
        if args.skip_test_watchos_simulator:
            impl_args += ["--skip-test-watchos-simulator"]
        if args.skip_test_android_host:
            impl_args += ["--skip-test-android-host"]
        if args.build_runtime_with_host_compiler:
            impl_args += ["--build-runtime-with-host-compiler"]
        if args.validation_test:
            impl_args += ["--validation-test"]
        if args.long_test:
            impl_args += ["--long-test"]
        if not args.benchmark:
            impl_args += ["--skip-test-benchmarks"]
        if not args.test_optimized:
            impl_args += ["--skip-test-optimized"]

        if args.android:
            impl_args += [
                "--android-ndk", args.android_ndk,
                "--android-api-level", args.android_api_level,
                "--android-ndk-gcc-version",
                args.android_ndk_gcc_version,
                "--android-icu-uc", args.android_icu_uc,
                "--android-icu-uc-include", args.android_icu_uc_include,
                "--android-icu-i18n", args.android_icu_i18n,
                "--android-icu-i18n-include", args.android_icu_i18n_include,
            ]
        if args.android_deploy_device_path:
            impl_args += [
                "--android-deploy-device-path",
                args.android_deploy_device_path,
            ]

        if platform.system() == 'Darwin':
            impl_args += [
                "--toolchain-prefix",
                targets.darwin_toolchain_prefix(
                    args.install_prefix),
                "--host-lipo", toolchain.lipo,
            ]

        if toolchain.libtool is not None:
            impl_args += [
                "--host-libtool", toolchain.libtool,
            ]

        # If we have extra_swift_args, combine all of them together and then
        # add them as one command.
        if args.extra_swift_args:
            impl_args += [
                "--extra-swift-args",
                ";".join(args.extra_swift_args)]

        # If we have extra_cmake_options, combine all of them together and then
        # add them as one command.
        if args.extra_cmake_options:
            impl_args += [
                "--extra-cmake-options=%s" % ' '.join(
                    pipes.quote(opt) for opt in args.extra_cmake_options)
            ]

        if args.lto_type is not None:
            impl_args += [
                "--llvm-enable-lto=%s" % args.lto_type,
                "--swift-tools-enable-lto=%s" % args.lto_type
            ]
            if args.llvm_max_parallel_lto_link_jobs is not None:
                impl_args += [
                    "--llvm-num-parallel-lto-link-jobs=%s" %
                    min(args.llvm_max_parallel_lto_link_jobs, args.build_jobs)
                ]
            if args.swift_tools_max_parallel_lto_link_jobs is not None:
                impl_args += [
                    "--swift-tools-num-parallel-lto-link-jobs=%s" %
                    min(args.swift_tools_max_parallel_lto_link_jobs,
                        args.build_jobs)
                ]

        impl_args += args.build_script_impl_args

        if args.dry_run:
            impl_args += ["--dry-run"]

        if args.clang_profile_instr_use:
            impl_args += [
                "--clang-profile-instr-use=%s" %
                os.path.abspath(args.clang_profile_instr_use)
            ]

        if args.lit_args:
            impl_args += ["--llvm-lit-args=%s" % args.lit_args]

        if args.coverage_db:
            impl_args += [
                "--coverage-db=%s" %
                os.path.abspath(args.coverage_db)
            ]

        # Compute the set of host-specific variables, which we pass through to
        # the build script via environment variables.
        host_specific_variables = self.compute_host_specific_variables()
        impl_env = {}
        for (host_target, options) in host_specific_variables.items():
            for (name, value) in options.items():
                # We mangle into an environment variable we can easily evaluate
                # from the `build-script-impl`.
                impl_env["HOST_VARIABLE_{}__{}".format(
                    host_target.replace("-", "_"), name)] = value

        return (impl_env, impl_args)

    def compute_host_specific_variables(self):
        """compute_host_specific_variables(args) -> dict

        Compute the host-specific options, organized as a dictionary keyed by
        host of options.
        """

        args = self.args

        options = {}
        for host_target in [args.host_target] + args.cross_compile_hosts:
            # Compute the host specific configuration.
            config = HostSpecificConfiguration(host_target, self)

            # Convert into `build-script-impl` style variables.
            options[host_target] = {
                "SWIFT_SDKS": " ".join(sorted(
                    config.sdks_to_configure)),
                "SWIFT_STDLIB_TARGETS": " ".join(
                    config.swift_stdlib_build_targets),
                "SWIFT_BENCHMARK_TARGETS": " ".join(
                    config.swift_benchmark_build_targets),
                "SWIFT_RUN_BENCHMARK_TARGETS": " ".join(
                    config.swift_benchmark_run_targets),
                "SWIFT_TEST_TARGETS": " ".join(
                    config.swift_test_run_targets),
            }

        return options

    def compute_product_classes(self):
        """compute_product_classes() -> list

        Compute the list of all Product classes used in this build. This list
        is constructed in dependency order.
        """

        # FIXME: This is a weird division (returning a list of class objects),
        # but it matches the existing structure of the `build-script-impl`.

        product_classes = []
        product_classes.append(products.CMark)
        product_classes.append(products.LLVM)
        if self.args.build_libicu:
            product_classes.append(products.LibICU)
        product_classes.append(products.Swift)
        if self.args.build_lldb:
            product_classes.append(products.LLDB)
        if self.args.build_llbuild:
            product_classes.append(products.LLBuild)
        if self.args.build_libdispatch:
            product_classes.append(products.LibDispatch)
        if self.args.build_foundation:
            product_classes.append(products.Foundation)
        if self.args.build_xctest:
            product_classes.append(products.XCTest)
        if self.args.build_swiftpm:
            product_classes.append(products.SwiftPM)
        return product_classes

    def execute(self):
        """Execute the invocation with the configured arguments."""

        # Convert to a build-script-impl invocation.
        (impl_env, impl_args) = self.convert_to_impl_arguments()

        # If using the legacy implementation, delegate all behavior to
        # `build-script-impl`.
        if self.args.legacy_impl:
            # Execute the underlying build script implementation.
            call_without_sleeping([build_script_impl] + impl_args,
                                  env=impl_env, echo=True)
            return

        # Otherwise, we compute and execute the individual actions ourselves.

        def execute_one_impl_action(host=None, product_class=None, name=None):
            if host is None:
                assert (product_class is None and
                        name == "merged-hosts-lipo"), "invalid action"
                action_name = name
            elif product_class is None:
                assert name == "package", "invalid action"
                action_name = "{}-{}".format(host.name, name)
            else:
                assert name is not None, "invalid action"
                action_name = "{}-{}-{}".format(
                    host.name, product_class.product_name(), name)
            call_without_sleeping(
                [build_script_impl] + impl_args + [
                    "--only-execute", action_name],
                env=impl_env, echo=self.args.verbose_build)

        # Compute the list of hosts to operate on.
        all_host_names = [
            self.args.host_target] + self.args.cross_compile_hosts
        all_hosts = [StdlibDeploymentTarget.get_target_for_name(name)
                     for name in all_host_names]

        # Compute the list of product classes to operate on.
        #
        # FIXME: This should really be per-host, but the current structure
        # matches that of `build-script-impl`.
        product_classes = self.compute_product_classes()

        # Execute each "pass".

        # Build...
        for host_target in all_hosts:
            # FIXME: We should only compute these once.
            config = HostSpecificConfiguration(host_target.name, self)
            print("Building the standard library for: {}".format(
                " ".join(config.swift_stdlib_build_targets)))
            if config.swift_test_run_targets and (
                    self.args.test or self.args.long_test):
                print("Running Swift tests for: {}".format(
                    " ".join(config.swift_test_run_targets)))
            if config.swift_benchmark_run_targets and self.args.benchmark:
                print("Running Swift benchmarks for: {}".format(
                    " ".join(config.swift_benchmark_run_targets)))

            for product_class in product_classes:
                execute_one_impl_action(host_target, product_class, "build")

        # Test...
        for host_target in all_hosts:
            for product_class in product_classes:
                execute_one_impl_action(host_target, product_class, "test")

        # Install...
        for host_target in all_hosts:
            for product_class in product_classes:
                execute_one_impl_action(host_target, product_class, "install")

        # Package...
        for host_target in all_hosts:
            execute_one_impl_action(host_target, name="package")

        # Lipo...
        execute_one_impl_action(name="merged-hosts-lipo")


# Provide a short delay so accidentally invoked clean builds can be canceled.
def clean_delay():
    sys.stdout.write('Starting clean build in  ')
    for i in range(3, 0, -1):
        sys.stdout.write('\b%d' % i)
        sys.stdout.flush()
        time.sleep(1)
    print('\b\b\b\bnow.')


# Main entry point for the preset mode.
def main_preset():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="""Builds Swift using a preset.""")
    parser.add_argument(
        "-n", "--dry-run",
        help="print the commands that would be executed, but do not execute "
             "them",
        action="store_true",
        default=False)
    parser.add_argument(
        "--preset-file",
        help="load presets from the specified file",
        metavar="PATH",
        action="append",
        dest="preset_file_names",
        default=[])
    parser.add_argument(
        "--preset",
        help="use the specified option preset",
        metavar="NAME")
    parser.add_argument(
        "--show-presets",
        help="list all presets and exit",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "--distcc",
        help="use distcc",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "-j", "--jobs",
        help="the number of parallel build jobs to use",
        type=int,
        dest="build_jobs")
    parser.add_argument(
        "preset_substitutions_raw",
        help="'name=value' pairs that are substituted in the preset",
        nargs="*",
        metavar="SUBSTITUTION")
    parser.add_argument(
        "--expand-build-script-invocation",
        help="Print the expanded build-script invocation generated "
             "by the preset, but do not run the preset",
        action=arguments.action.optional_bool)
    args = parser.parse_args()

    if len(args.preset_file_names) == 0:
        args.preset_file_names = [
            os.path.join(HOME, ".swift-build-presets"),
            os.path.join(
                SWIFT_SOURCE_ROOT, SWIFT_REPO_NAME, "utils",
                "build-presets.ini")
        ]

    if args.show_presets:
        for name in sorted(get_all_preset_names(args.preset_file_names),
                           key=str.lower):
            print(name)
        return 0

    if not args.preset:
        diagnostics.fatal("missing --preset option")

    args.preset_substitutions = {}

    for arg in args.preset_substitutions_raw:
        name, value = arg.split("=", 1)
        args.preset_substitutions[name] = value

    preset_args = get_preset_options(
        args.preset_substitutions, args.preset_file_names, args.preset)

    build_script_args = [sys.argv[0]]
    if args.dry_run:
        build_script_args += ["--dry-run"]
    build_script_args += preset_args
    if args.distcc:
        build_script_args += ["--distcc"]
    if args.build_jobs:
        build_script_args += ["--jobs", str(args.build_jobs)]

    diagnostics.note(
        "using preset '" + args.preset + "', which expands to \n\n" +
        shell.quote_command(build_script_args) + "\n")

    if args.expand_build_script_invocation:
        return 0

    call_without_sleeping(build_script_args)
    return 0


# Main entry point for the normal mode.
def main_normal():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        usage="""
  %(prog)s [-h | --help] [OPTION ...]
  %(prog)s --preset=NAME [SUBSTITUTION ...]
""",
        description="""
Use this tool to build, test, and prepare binary distribution archives of Swift
and related tools.

Builds Swift (and, optionally, LLDB), incrementally, optionally
testing it thereafter.  Different build configurations are maintained in
parallel.""",
        epilog="""
Using option presets:

  --preset-file=PATH    load presets from the specified file

  --preset=NAME         use the specified option preset

  The preset mode is mutually exclusive with other options.  It is not
  possible to add ad-hoc customizations to a preset.  This is a deliberate
  design decision.  (Rationale: a preset is a certain important set of
  options that we want to keep in a centralized location.  If you need to
  customize it, you should create another preset in a centralized location,
  rather than scattering the knowledge about the build across the system.)

  Presets support substitutions for controlled customizations.  Substitutions
  are defined in the preset file.  Values for substitutions are supplied
  using the name=value syntax on the command line.


Any arguments not listed are forwarded directly to Swift's
'build-script-impl'.  See that script's help for details.

Environment variables
---------------------

This script respects a few environment variables, should you
choose to set them:

SWIFT_SOURCE_ROOT: a directory containing the source for LLVM, Clang, Swift.
                   If this script is located in a Swift
                   source directory, the location of SWIFT_SOURCE_ROOT will be
                   inferred if the variable is not set.

'build-script' expects the sources to be laid out in the following way:

   $SWIFT_SOURCE_ROOT/llvm
                     /clang
                     /swift
                     /lldb                       (optional)
                     /llbuild                    (optional)
                     /swiftpm                    (optional, requires llbuild)
                     /compiler-rt                (optional)
                     /swift-corelibs-xctest      (optional)
                     /swift-corelibs-foundation  (optional)
                     /swift-corelibs-libdispatch (optional)
                     /icu                        (optional)

SWIFT_BUILD_ROOT: a directory in which to create out-of-tree builds.
                  Defaults to "$SWIFT_SOURCE_ROOT/build/".

Preparing to run this script
----------------------------

  See README.md for instructions on cloning Swift subprojects.

If you intend to use the -l, -L, --lldb, or --debug-lldb options.

That's it; you're ready to go!

Examples
--------

Given the above layout of sources, the simplest invocation of 'build-script' is
just:

  [~/src/s]$ ./swift/utils/build-script

This builds LLVM, Clang, Swift and Swift standard library in debug mode.

All builds are incremental.  To incrementally build changed files, repeat the
same 'build-script' command.

Typical uses of 'build-script'
------------------------------

To build everything with optimization without debug information:

  [~/src/s]$ ./swift/utils/build-script -R

To run tests, add '-t':

  [~/src/s]$ ./swift/utils/build-script -R -t

To run normal tests and validation tests, add '-T':

  [~/src/s]$ ./swift/utils/build-script -R -T

To build LLVM+Clang with optimization without debug information, and a
debuggable Swift compiler:

  [~/src/s]$ ./swift/utils/build-script -R --debug-swift

To build a debuggable Swift standard library:

  [~/src/s]$ ./swift/utils/build-script -R --debug-swift-stdlib

iOS build targets are always configured and present, but are not built by
default.  To build the standard library for OS X, iOS simulator and iOS device:

  [~/src/s]$ ./swift/utils/build-script -R -i

To run OS X and iOS tests that don't require a device:

  [~/src/s]$ ./swift/utils/build-script -R -i -t

To use 'make' instead of 'ninja', use '-m':

  [~/src/s]$ ./swift/utils/build-script -m -R

To create Xcode projects that can build Swift, use '-x':

  [~/src/s]$ ./swift/utils/build-script -x -R

Preset mode in build-script
---------------------------

All buildbots and automated environments use 'build-script' in *preset mode*.
In preset mode, the command line only specifies the preset name and allows
limited customization (extra output paths).  The actual options come from
the selected preset in 'utils/build-presets.ini'.  For example, to build like
the incremental buildbot, run:

  [~/src/s]$ ./swift/utils/build-script --preset=buildbot_incremental

To build with AddressSanitizer:

  [~/src/s]$ ./swift/utils/build-script --preset=asan

To build a root for Xcode XYZ, '/tmp/xcode-xyz-root.tar.gz':

  [~/src/s]$ ./swift/utils/build-script --preset=buildbot_BNI_internal_XYZ \\
      install_destdir="/tmp/install"
      install_symroot="/tmp/symroot"
      installable_package="/tmp/xcode-xyz-root.tar.gz"

If you have your own favorite set of options, you can create your own, local,
preset.  For example, let's create a preset called 'ds' (which stands for
Debug Swift):

  $ cat > ~/.swift-build-presets
  [preset: ds]
  release
  debug-swift
  debug-swift-stdlib
  test
  build-subdir=ds

To use it, specify the '--preset=' argument:

  [~/src/s]$ ./swift/utils/build-script --preset=ds
  ./swift/utils/build-script: using preset 'ds', which expands to
  ./swift/utils/build-script --release --debug-swift --debug-swift-stdlib \
     --test
  --build-subdir=ds --
  ...

Philosophy
----------

While you can invoke CMake directly to build Swift, this tool will save you
time by taking away the mechanical parts of the process, providing you controls
for the important options.

For all automated build environments, this tool is regarded as *the* *only* way
to build Swift.  This is not a technical limitation of the Swift build system.
It is a policy decision aimed at making the builds uniform across all
environments and easily reproducible by engineers who are not familiar with the
details of the setups of other systems or automated environments.""")

    parser.add_argument(
        "-n", "--dry-run",
        help="print the commands that would be executed, but do not execute "
             "them",
        action="store_true",
        default=False)
    parser.add_argument(
        "--no-legacy-impl", dest="legacy_impl",
        help="avoid legacy implementation",
        action="store_false",
        default=True)

    targets_group = parser.add_argument_group(
        title="Host and cross-compilation targets")
    targets_group.add_argument(
        "--host-target",
        help="The host target. LLVM, Clang, and Swift will be built for this "
             "target. The built LLVM and Clang will be used to compile Swift "
             "for the cross-compilation targets.",
        default=StdlibDeploymentTarget.host_target().name)
    targets_group.add_argument(
        "--cross-compile-hosts",
        help="A space separated list of targets to cross-compile host Swift "
             "tools for. Can be used multiple times.",
        action=arguments.action.concat, type=arguments.type.shell_split,
        default=[])
    targets_group.add_argument(
        "--stdlib-deployment-targets",
        help="list of targets to compile or cross-compile the Swift standard "
             "library for. %(default)s by default.",
        action=arguments.action.concat, type=arguments.type.shell_split,
        default=None)
    targets_group.add_argument(
        "--build-stdlib-deployment-targets",
        help="A space-separated list that filters which of the configured "
             "targets to build the Swift standard library for, or 'all'.",
        type=arguments.type.shell_split, default=["all"])

    projects_group = parser.add_argument_group(
        title="Options to select projects")
    projects_group.add_argument(
        "-l", "--lldb",
        help="build LLDB",
        action="store_true",
        dest="build_lldb")
    projects_group.add_argument(
        "-b", "--llbuild",
        help="build llbuild",
        action="store_true",
        dest="build_llbuild")
    projects_group.add_argument(
        "-p", "--swiftpm",
        help="build swiftpm",
        action="store_true",
        dest="build_swiftpm")
    projects_group.add_argument(
        "--xctest",
        help="build xctest",
        action=arguments.action.optional_bool,
        dest="build_xctest")
    projects_group.add_argument(
        "--foundation",
        help="build foundation",
        action=arguments.action.optional_bool,
        dest="build_foundation")
    projects_group.add_argument(
        "--libdispatch",
        help="build libdispatch",
        action=arguments.action.optional_bool,
        dest="build_libdispatch")
    projects_group.add_argument(
        "--libicu",
        help="build libicu",
        action=arguments.action.optional_bool,
        dest="build_libicu")
    projects_group.add_argument(
        "--playgroundlogger",
        help="build playgroundlogger",
        action="store_true",
        dest="build_playgroundlogger")
    projects_group.add_argument(
        "--playgroundsupport",
        help="build PlaygroundSupport",
        action="store_true",
        dest="build_playgroundsupport")
    projects_group.add_argument(
        "--build-ninja",
        help="build the Ninja tool",
        action=arguments.action.optional_bool)

    extra_actions_group = parser.add_argument_group(
        title="Extra actions to perform before or in addition to building")
    extra_actions_group.add_argument(
        "-c", "--clean",
        help="do a clean build",
        action="store_true")
    extra_actions_group.add_argument(
        "--export-compile-commands",
        help="generate compilation databases in addition to building",
        action=arguments.action.optional_bool)
    extra_actions_group.add_argument(
        "--symbols-package",
        metavar="PATH",
        help="if provided, an archive of the symbols directory will be "
             "generated at this path")

    build_variant_group = parser.add_mutually_exclusive_group(required=False)
    build_variant_group.add_argument(
        "-d", "--debug",
        help="build the Debug variant of everything (LLVM, Clang, Swift host "
             "tools, target Swift standard libraries, LLDB (if enabled) "
             "(default)",
        action="store_const",
        const="Debug",
        dest="build_variant")
    build_variant_group.add_argument(
        "-r", "--release-debuginfo",
        help="build the RelWithDebInfo variant of everything (default is "
             "Debug)",
        action="store_const",
        const="RelWithDebInfo",
        dest="build_variant")
    build_variant_group.add_argument(
        "-R", "--release",
        help="build the Release variant of everything (default is Debug)",
        action="store_const",
        const="Release",
        dest="build_variant")

    build_variant_override_group = parser.add_argument_group(
        title="Override build variant for a specific project")
    build_variant_override_group.add_argument(
        "--debug-llvm",
        help="build the Debug variant of LLVM",
        action="store_const",
        const="Debug",
        dest="llvm_build_variant")
    build_variant_override_group.add_argument(
        "--debug-swift",
        help="build the Debug variant of Swift host tools",
        action="store_const",
        const="Debug",
        dest="swift_build_variant")
    build_variant_override_group.add_argument(
        "--debug-swift-stdlib",
        help="build the Debug variant of the Swift standard library and SDK "
             "overlay",
        action="store_const",
        const="Debug",
        dest="swift_stdlib_build_variant")
    build_variant_override_group.add_argument(
        "--debug-lldb",
        help="build the Debug variant of LLDB",
        action="store_const",
        const="Debug",
        dest="lldb_build_variant")
    build_variant_override_group.add_argument(
        "--debug-cmark",
        help="build the Debug variant of CommonMark",
        action="store_const",
        const="Debug",
        dest="cmark_build_variant")
    build_variant_override_group.add_argument(
        "--debug-foundation",
        help="build the Debug variant of Foundation",
        action="store_const",
        const="Debug",
        dest="foundation_build_variant")
    build_variant_override_group.add_argument(
        "--debug-libdispatch",
        help="build the Debug variant of libdispatch",
        action="store_const",
        const="Debug",
        dest="libdispatch_build_variant")
    build_variant_override_group.add_argument(
        "--debug-libicu",
        help="build the Debug variant of libicu",
        action="store_const",
        const="Debug",
        dest="libicu_build_variant")

    assertions_group = parser.add_mutually_exclusive_group(required=False)
    assertions_group.add_argument(
        "--assertions",
        help="enable assertions in all projects",
        action="store_const",
        const=True,
        dest="assertions")
    assertions_group.add_argument(
        "--no-assertions",
        help="disable assertions in all projects",
        action="store_const",
        const=False,
        dest="assertions")

    assertions_override_group = parser.add_argument_group(
        title="Control assertions in a specific project")
    assertions_override_group.add_argument(
        "--cmark-assertions",
        help="enable assertions in CommonMark",
        action="store_const",
        const=True,
        dest="cmark_assertions")
    assertions_override_group.add_argument(
        "--llvm-assertions",
        help="enable assertions in LLVM",
        action="store_const",
        const=True,
        dest="llvm_assertions")
    assertions_override_group.add_argument(
        "--no-llvm-assertions",
        help="disable assertions in LLVM",
        action="store_const",
        const=False,
        dest="llvm_assertions")
    assertions_override_group.add_argument(
        "--swift-assertions",
        help="enable assertions in Swift",
        action="store_const",
        const=True,
        dest="swift_assertions")
    assertions_override_group.add_argument(
        "--no-swift-assertions",
        help="disable assertions in Swift",
        action="store_const",
        const=False,
        dest="swift_assertions")
    assertions_override_group.add_argument(
        "--swift-stdlib-assertions",
        help="enable assertions in the Swift standard library",
        action="store_const",
        const=True,
        dest="swift_stdlib_assertions")
    assertions_override_group.add_argument(
        "--no-swift-stdlib-assertions",
        help="disable assertions in the Swift standard library",
        action="store_const",
        const=False,
        dest="swift_stdlib_assertions")
    assertions_override_group.add_argument(
        "--lldb-assertions",
        help="enable assertions in LLDB",
        action="store_const",
        const=True,
        dest="lldb_assertions")
    assertions_override_group.add_argument(
        "--no-lldb-assertions",
        help="disable assertions in LLDB",
        action="store_const",
        const=False,
        dest="lldb_assertions")

    # FIXME: This should be one option using choices=[...]
    cmake_generator_group = parser.add_argument_group(
        title="Select the CMake generator")
    cmake_generator_group.add_argument(
        "-x", "--xcode",
        help="use CMake's Xcode generator (default is Ninja)",
        action="store_const",
        const="Xcode",
        dest="cmake_generator")
    cmake_generator_group.add_argument(
        "-m", "--make",
        help="use CMake's Makefile generator (default is Ninja)",
        action="store_const",
        const="Unix Makefiles",
        dest="cmake_generator")
    cmake_generator_group.add_argument(
        "-e", "--eclipse",
        help="use CMake's Eclipse generator (default is Ninja)",
        action="store_const",
        const="Eclipse CDT4 - Ninja",
        dest="cmake_generator")

    run_tests_group = parser.add_argument_group(
        title="Run tests")

    # NOTE: We can't merge -t and --test, because nargs='?' makes
    #       `-ti` to be treated as `-t=i`.
    run_tests_group.add_argument(
        "-t",
        help="test Swift after building",
        action="store_const",
        const=True,
        dest="test")
    run_tests_group.add_argument(
        "--test",
        help="test Swift after building",
        action=arguments.action.optional_bool)
    run_tests_group.add_argument(
        "-T",
        help="run the validation test suite (implies --test)",
        action="store_const",
        const=True,
        dest="validation_test")
    run_tests_group.add_argument(
        "--validation-test",
        help="run the validation test suite (implies --test)",
        action=arguments.action.optional_bool)
    run_tests_group.add_argument(
        "-o",
        help="run the test suite in optimized mode too (implies --test)",
        action="store_const",
        const=True,
        dest="test_optimized")
    run_tests_group.add_argument(
        "--test-optimized",
        help="run the test suite in optimized mode too (implies --test)",
        action=arguments.action.optional_bool)
    run_tests_group.add_argument(
        "--long-test",
        help="run the long test suite",
        action=arguments.action.optional_bool)
    run_tests_group.add_argument(
        "--host-test",
        help="run executable tests on host devices (such as iOS or tvOS)",
        action=arguments.action.optional_bool)
    run_tests_group.add_argument(
        "-B", "--benchmark",
        help="run the Swift Benchmark Suite after building",
        action="store_true")
    run_tests_group.add_argument(
        "--benchmark-num-o-iterations",
        help="if the Swift Benchmark Suite is run after building, run N \
iterations with -O",
        metavar='N', type=int, default=3)
    run_tests_group.add_argument(
        "--benchmark-num-onone-iterations",
        help="if the Swift Benchmark Suite is run after building, run N \
        iterations with -Onone", metavar='N', type=int, default=3)
    run_tests_group.add_argument(
        "--skip-test-osx",
        help="skip testing Swift stdlibs for Mac OS X",
        action=arguments.action.optional_bool)
    run_tests_group.add_argument(
        "--skip-test-linux",
        help="skip testing Swift stdlibs for Linux",
        action=arguments.action.optional_bool)
    run_tests_group.add_argument(
        "--skip-test-freebsd",
        help="skip testing Swift stdlibs for FreeBSD",
        action=arguments.action.optional_bool)
    run_tests_group.add_argument(
        "--skip-test-cygwin",
        help="skip testing Swift stdlibs for Cygwin",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "--build-runtime-with-host-compiler",
        help="Use the host compiler, not the self-built one to compile the "
             "Swift runtime",
        action=arguments.action.optional_bool)

    run_build_group = parser.add_argument_group(
        title="Run build")
    run_build_group.add_argument(
        "--build-swift-dynamic-stdlib",
        help="build dynamic variants of the Swift standard library",
        action=arguments.action.optional_bool,
        default=True)
    run_build_group.add_argument(
        "--build-swift-static-stdlib",
        help="build static variants of the Swift standard library",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--build-swift-dynamic-sdk-overlay",
        help="build dynamic variants of the Swift SDK overlay",
        action=arguments.action.optional_bool,
        default=True)
    run_build_group.add_argument(
        "--build-swift-static-sdk-overlay",
        help="build static variants of the Swift SDK overlay",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--build-swift-stdlib-unittest-extra",
        help="Build optional StdlibUnittest components",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "-S", "--skip-build",
        help="generate build directory only without building",
        action="store_true")
    run_build_group.add_argument(
        "--skip-build-linux",
        help="skip building Swift stdlibs for Linux",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-freebsd",
        help="skip building Swift stdlibs for FreeBSD",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-cygwin",
        help="skip building Swift stdlibs for Cygwin",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-osx",
        help="skip building Swift stdlibs for MacOSX",
        action=arguments.action.optional_bool)

    run_build_group.add_argument(
        "--skip-build-ios",
        help="skip building Swift stdlibs for iOS",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-ios-device",
        help="skip building Swift stdlibs for iOS devices "
             "(i.e. build simulators only)",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-ios-simulator",
        help="skip building Swift stdlibs for iOS simulator "
             "(i.e. build devices only)",
        action=arguments.action.optional_bool)

    run_build_group.add_argument(
        "--skip-build-tvos",
        help="skip building Swift stdlibs for tvOS",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-tvos-device",
        help="skip building Swift stdlibs for tvOS devices "
             "(i.e. build simulators only)",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-tvos-simulator",
        help="skip building Swift stdlibs for tvOS simulator "
             "(i.e. build devices only)",
        action=arguments.action.optional_bool)

    run_build_group.add_argument(
        "--skip-build-watchos",
        help="skip building Swift stdlibs for watchOS",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-watchos-device",
        help="skip building Swift stdlibs for watchOS devices "
             "(i.e. build simulators only)",
        action=arguments.action.optional_bool)
    run_build_group.add_argument(
        "--skip-build-watchos-simulator",
        help="skip building Swift stdlibs for watchOS simulator "
             "(i.e. build devices only)",
        action=arguments.action.optional_bool)

    run_build_group.add_argument(
        "--skip-build-android",
        help="skip building Swift stdlibs for Android",
        action=arguments.action.optional_bool)

    run_build_group.add_argument(
        "--skip-build-benchmarks",
        help="skip building Swift Benchmark Suite",
        action=arguments.action.optional_bool)

    skip_test_group = parser.add_argument_group(
        title="Skip testing specified targets")
    skip_test_group.add_argument(
        "--skip-test-ios",
        help="skip testing all iOS targets. Equivalent to specifying both "
             "--skip-test-ios-simulator and --skip-test-ios-host",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-ios-simulator",
        help="skip testing iOS simulator targets",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-ios-host",
        help="skip testing iOS device targets on the host machine (the phone "
             "itself)",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-tvos",
        help="skip testing all tvOS targets. Equivalent to specifying both "
             "--skip-test-tvos-simulator and --skip-test-tvos-host",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-tvos-simulator",
        help="skip testing tvOS simulator targets",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-tvos-host",
        help="skip testing tvOS device targets on the host machine (the TV "
             "itself)",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-watchos",
        help="skip testing all tvOS targets. Equivalent to specifying both "
             "--skip-test-watchos-simulator and --skip-test-watchos-host",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-watchos-simulator",
        help="skip testing watchOS simulator targets",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-watchos-host",
        help="skip testing watchOS device targets on the host machine (the "
             "watch itself)",
        action=arguments.action.optional_bool)
    skip_test_group.add_argument(
        "--skip-test-android-host",
        help="skip testing Android device targets on the host machine (the "
             "phone itself)",
        action=arguments.action.optional_bool)

    parser.add_argument(
        "-i", "--ios",
        help="also build for iOS, but disallow tests that require an iOS "
             "device",
        action="store_true")
    parser.add_argument(
        "-I", "--ios-all",
        help="also build for iOS, and allow all iOS tests",
        action="store_true",
        dest="ios_all")
    parser.add_argument(
        "--skip-ios",
        help="set to skip everything iOS-related",
        dest="ios",
        action="store_false")

    parser.add_argument(
        "--tvos",
        help="also build for tvOS, but disallow tests that require a tvos "
             "device",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "--tvos-all",
        help="also build for tvOS, and allow all tvOS tests",
        action=arguments.action.optional_bool,
        dest="tvos_all")
    parser.add_argument(
        "--skip-tvos",
        help="set to skip everything tvOS-related",
        dest="tvos",
        action="store_false")

    parser.add_argument(
        "--watchos",
        help="also build for watchOS, but disallow tests that require an "
             "watchOS device",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "--watchos-all",
        help="also build for Apple watchOS, and allow all Apple watchOS tests",
        action=arguments.action.optional_bool,
        dest="watchos_all")
    parser.add_argument(
        "--skip-watchos",
        help="set to skip everything watchOS-related",
        dest="watchos",
        action="store_false")

    parser.add_argument(
        "--android",
        help="also build for Android",
        action=arguments.action.optional_bool)

    parser.add_argument(
        "--swift-analyze-code-coverage",
        help="enable code coverage analysis in Swift (false, not-merged, "
             "merged).",
        choices=["false", "not-merged", "merged"],
        default="false",  # so CMake can see the inert mode as a false value
        dest="swift_analyze_code_coverage")

    parser.add_argument(
        "--build-subdir",
        help="name of the directory under $SWIFT_BUILD_ROOT where the build "
             "products will be placed",
        metavar="PATH")
    parser.add_argument(
        "--install-prefix",
        help="The installation prefix. This is where built Swift products "
             "(like bin, lib, and include) will be installed.",
        metavar="PATH",
        default=targets.install_prefix())
    parser.add_argument(
        "--install-symroot",
        help="the path to install debug symbols into",
        metavar="PATH")

    parser.add_argument(
        "-j", "--jobs",
        help="the number of parallel build jobs to use",
        type=int,
        dest="build_jobs",
        default=multiprocessing.cpu_count())

    parser.add_argument(
        "--darwin-xcrun-toolchain",
        help="the name of the toolchain to use on Darwin",
        default="default")
    parser.add_argument(
        "--cmake",
        help="the path to a CMake executable that will be used to build "
             "Swift",
        type=arguments.type.executable,
        metavar="PATH")
    parser.add_argument(
        "--show-sdks",
        help="print installed Xcode and SDK versions",
        action=arguments.action.optional_bool)

    parser.add_argument(
        "--extra-swift-args",
        help="Pass through extra flags to swift in the form of a cmake list "
             "'module_regexp;flag'. Can be called multiple times to add "
             "multiple such module_regexp flag pairs. All semicolons in flags "
             "must be escaped with a '\\'",
        action="append", dest="extra_swift_args", default=[])

    llvm_group = parser.add_argument_group(
        title="Build settings specific for LLVM")
    llvm_group.add_argument(
        '--llvm-targets-to-build',
        help='LLVM target generators to build',
        default="X86;ARM;AArch64;PowerPC;SystemZ")

    android_group = parser.add_argument_group(
        title="Build settings for Android")
    android_group.add_argument(
        "--android-ndk",
        help="An absolute path to the NDK that will be used as a libc "
             "implementation for Android builds",
        metavar="PATH")
    android_group.add_argument(
        "--android-api-level",
        help="The Android API level to target when building for Android. "
             "Currently only 21 or above is supported",
        default="21")
    android_group.add_argument(
        "--android-ndk-gcc-version",
        help="The GCC version to use when building for Android. Currently "
             "only 4.9 is supported. %(default)s is also the default value. "
             "This option may be used when experimenting with versions "
             "of the Android NDK not officially supported by Swift",
        choices=["4.8", "4.9"],
        default="4.9")
    android_group.add_argument(
        "--android-icu-uc",
        help="Path to a directory containing libicuuc.so",
        metavar="PATH")
    android_group.add_argument(
        "--android-icu-uc-include",
        help="Path to a directory containing headers for libicuuc",
        metavar="PATH")
    android_group.add_argument(
        "--android-icu-i18n",
        help="Path to a directory containing libicui18n.so",
        metavar="PATH")
    android_group.add_argument(
        "--android-icu-i18n-include",
        help="Path to a directory containing headers libicui18n",
        metavar="PATH")
    android_group.add_argument(
        "--android-deploy-device-path",
        help="Path on an Android device to which built Swift stdlib products "
             "will be deployed. If running host tests, specify the '{}' "
             "directory.".format(android.adb.commands.DEVICE_TEMP_DIR),
        default=android.adb.commands.DEVICE_TEMP_DIR,
        metavar="PATH")

    parser.add_argument(
        "--host-cc",
        help="the absolute path to CC, the 'clang' compiler for the host "
             "platform. Default is auto detected.",
        type=arguments.type.executable,
        metavar="PATH")
    parser.add_argument(
        "--host-cxx",
        help="the absolute path to CXX, the 'clang++' compiler for the host "
             "platform. Default is auto detected.",
        type=arguments.type.executable,
        metavar="PATH")
    parser.add_argument(
        "--host-lipo",
        help="the absolute path to lipo. Default is auto detected.",
        type=arguments.type.executable,
        metavar="PATH")
    parser.add_argument(
        "--host-libtool",
        help="the absolute path to libtool. Default is auto detected.",
        type=arguments.type.executable,
        metavar="PATH")
    parser.add_argument(
        "--distcc",
        help="use distcc in pump mode",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "--enable-asan",
        help="enable Address Sanitizer",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "--enable-ubsan",
        help="enable Undefined Behavior Sanitizer",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "--enable-tsan",
        help="enable Thread Sanitizer for swift tools",
        action=arguments.action.optional_bool)
    parser.add_argument(
        "--enable-tsan-runtime",
        help="enable Thread Sanitizer on the swift runtime")

    parser.add_argument(
        "--compiler-vendor",
        choices=["none", "apple"],
        default="none",
        help="Compiler vendor name")
    parser.add_argument(
        "--clang-compiler-version",
        help="string that indicates a compiler version for Clang",
        type=arguments.type.clang_compiler_version,
        metavar="MAJOR.MINOR.PATCH")
    parser.add_argument(
        "--clang-user-visible-version",
        help="User-visible version of the embedded Clang and LLVM compilers",
        type=arguments.type.clang_compiler_version,
        default="4.0.0",
        metavar="MAJOR.MINOR.PATCH")
    parser.add_argument(
        "--swift-compiler-version",
        help="string that indicates a compiler version for Swift",
        type=arguments.type.swift_compiler_version,
        metavar="MAJOR.MINOR")
    parser.add_argument(
        "--swift-user-visible-version",
        help="User-visible version of the embedded Swift compiler",
        type=arguments.type.swift_compiler_version,
        default="4.0",
        metavar="MAJOR.MINOR")

    parser.add_argument(
        "--darwin-deployment-version-osx",
        help="minimum deployment target version for OS X",
        metavar="MAJOR.MINOR",
        default="10.9")
    parser.add_argument(
        "--darwin-deployment-version-ios",
        help="minimum deployment target version for iOS",
        metavar="MAJOR.MINOR",
        default="7.0")
    parser.add_argument(
        "--darwin-deployment-version-tvos",
        help="minimum deployment target version for tvOS",
        metavar="MAJOR.MINOR",
        default="9.0")
    parser.add_argument(
        "--darwin-deployment-version-watchos",
        help="minimum deployment target version for watchOS",
        metavar="MAJOR.MINOR",
        default="2.0")

    parser.add_argument(
        "--extra-cmake-options",
        help="Pass through extra options to CMake in the form of comma "
             "separated options '-DCMAKE_VAR1=YES,-DCMAKE_VAR2=/tmp'. Can be "
             "called multiple times to add multiple such options.",
        action=arguments.action.concat,
        type=arguments.type.shell_split,
        default=[])

    parser.add_argument(
        "--build-args",
        help="arguments to the build tool. This would be prepended to the "
             "default argument that is '-j8' when CMake generator is "
             "\"Ninja\".",
        type=arguments.type.shell_split,
        default=[])

    parser.add_argument(
        "--verbose-build",
        help="print the commands executed during the build",
        action=arguments.action.optional_bool)

    parser.add_argument(
        "--lto",
        help="use lto optimization on llvm/swift tools. This does not "
             "imply using lto on the swift standard library or runtime. "
             "Options: thin, full. If no optional arg is provided, full is "
             "chosen by default",
        metavar="LTO_TYPE",
        nargs='?',
        choices=['thin', 'full'],
        default=None,
        const='full',
        dest='lto_type')

    parser.add_argument(
        "--clang-profile-instr-use",
        help="profile file to use for clang PGO",
        metavar="PATH")

    default_max_lto_link_job_counts = host.max_lto_link_job_counts()
    parser.add_argument(
        "--llvm-max-parallel-lto-link-jobs",
        help="the maximum number of parallel link jobs to use when compiling "
             "llvm",
        metavar="COUNT",
        default=default_max_lto_link_job_counts['llvm'])

    parser.add_argument(
        "--swift-tools-max-parallel-lto-link-jobs",
        help="the maximum number of parallel link jobs to use when compiling "
             "swift tools.",
        metavar="COUNT",
        default=default_max_lto_link_job_counts['swift'])

    parser.add_argument("--enable-sil-ownership",
                        help="Enable the SIL ownership model",
                        action='store_true')

    parser.add_argument("--force-optimized-typechecker",
                        help="Force the type checker to be built with "
                        "optimization",
                        action='store_true')

    parser.add_argument(
        # Explicitly unavailable options here.
        "--build-jobs",
        "--common-cmake-options",
        "--only-execute",
        "--skip-test-optimized",
        action=arguments.action.unavailable)

    parser.add_argument(
        "--lit-args",
        help="lit args to use when testing",
        metavar="LITARGS",
        default="-sv")

    parser.add_argument(
        "--coverage-db",
        help="coverage database to use when prioritizing testing",
        metavar="PATH")

    args = migration.parse_args(parser, sys.argv[1:])

    if args.build_script_impl_args:
        # If we received any impl args, check if `build-script-impl` would
        # accept them or not before any further processing.
        try:
            migration.check_impl_args(build_script_impl,
                                      args.build_script_impl_args)
        except ValueError as e:
            exit_rejecting_arguments(e, parser)

        if '--check-args-only' in args.build_script_impl_args:
            return 0

    shell.dry_run = args.dry_run

    # Prepare and validate toolchain
    toolchain = host_toolchain(xcrun_toolchain=args.darwin_xcrun_toolchain)
    os.environ['TOOLCHAINS'] = args.darwin_xcrun_toolchain

    if args.host_cc is not None:
        toolchain.cc = args.host_cc
    if args.host_cxx is not None:
        toolchain.cxx = args.host_cxx
    if args.host_lipo is not None:
        toolchain.lipo = args.host_lipo
    if args.host_libtool is not None:
        toolchain.libtool = args.host_libtool
    if args.cmake is not None:
        toolchain.cmake = args.cmake

    # Preprocess the arguments to apply defaults.
    BuildScriptInvocation.apply_default_arguments(toolchain, args)

    # Validate the arguments.
    BuildScriptInvocation.validate_arguments(toolchain, args)

    # Create the build script invocation.
    invocation = BuildScriptInvocation(toolchain, args)

    # Sanitize the runtime environment.
    invocation.initialize_runtime_environment()

    # Show SDKs, if requested.
    if args.show_sdks:
        debug.print_xcodebuild_versions()

    # Clean build directory if requested.
    if args.clean:
        clean_delay()
        shell.rmtree(invocation.workspace.build_root)

    # Create build directory.
    shell.makedirs(invocation.workspace.build_root)

    # Build ninja if required, which will update the toolchain.
    if args.build_ninja:
        invocation.build_ninja()

    # Execute the underlying build script implementation.
    invocation.execute()

    if args.symbols_package:
        print('--- Creating symbols package ---')
        print('-- Package file: {} --'.format(args.symbols_package))

        if platform.system() == 'Darwin':
            prefix = targets.darwin_toolchain_prefix(args.install_prefix)
        else:
            prefix = args.install_prefix

        # As a security measure, `tar` normally strips leading '/' from paths
        # it is archiving. To stay safe, we change working directories, then
        # run `tar` without the leading '/' (we remove it ourselves to keep
        # `tar` from emitting a warning).
        with shell.pushd(args.install_symroot):
            tar.tar(source=prefix.lstrip('/'),
                    destination=args.symbols_package)

    return 0


def main():
    if not SWIFT_SOURCE_ROOT:
        diagnostics.fatal(
            "could not infer source root directory " +
            "(forgot to set $SWIFT_SOURCE_ROOT environment variable?)")

    if not os.path.isdir(SWIFT_SOURCE_ROOT):
        diagnostics.fatal(
            "source root directory \'" + SWIFT_SOURCE_ROOT +
            "\' does not exist " +
            "(forgot to set $SWIFT_SOURCE_ROOT environment variable?)")

    # Determine if we are invoked in the preset mode and dispatch accordingly.
    if any([(opt.startswith("--preset") or opt == "--show-presets")
            for opt in sys.argv[1:]]):
        return main_preset()
    else:
        return main_normal()


if __name__ == "__main__":
    sys.exit(main())
